home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 44 / PC Actual CD 44.iso / Linux / Cygwin / full.exe / Disk1 / data1.cab / Tools / share / tk8.0 / tkfbox.tcl < prev    next >
Encoding:
Text File  |  1998-12-04  |  42.0 KB  |  1,649 lines

  1. # tkfbox.tcl --
  2. #
  3. #    Implements the "TK" standard file selection dialog box. This
  4. #    dialog box is used on the Unix platforms whenever the tk_strictMotif
  5. #    flag is not set.
  6. #
  7. #    The "TK" standard file selection dialog box is similar to the
  8. #    file selection dialog box on Win95(TM). The user can navigate
  9. #    the directories by clicking on the folder icons or by
  10. #    selectinf the "Directory" option menu. The user can select
  11. #    files by clicking on the file icons or by entering a filename
  12. #    in the "Filename:" entry.
  13. #
  14. # SCCS: @(#) tkfbox.tcl 1.13 97/10/01 14:51:01
  15. #
  16. # Copyright (c) 1994-1996 Sun Microsystems, Inc.
  17. #
  18. # See the file "license.terms" for information on usage and redistribution
  19. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  20. #
  21.  
  22. #----------------------------------------------------------------------
  23. #
  24. #              I C O N   L I S T
  25. #
  26. # This is a pseudo-widget that implements the icon list inside the 
  27. # tkFDialog dialog box.
  28. #
  29. #----------------------------------------------------------------------
  30.  
  31. # tkIconList --
  32. #
  33. #    Creates an IconList widget.
  34. #
  35. proc tkIconList {w args} {
  36.     upvar #0 $w data
  37.  
  38.     tkIconList_Config $w $args
  39.     tkIconList_Create $w
  40. }
  41.  
  42. # tkIconList_Config --
  43. #
  44. #    Configure the widget variables of IconList, according to the command
  45. #    line arguments.
  46. #
  47. proc tkIconList_Config {w argList} {
  48.     upvar #0 $w data
  49.  
  50.     # 1: the configuration specs
  51.     #
  52.     set specs {
  53.     {-browsecmd "" "" ""}
  54.     {-command "" "" ""}
  55.     {-multiple "" "" "0"}
  56.     }
  57.  
  58.     # 2: parse the arguments
  59.     #
  60.     tclParseConfigSpec $w $specs "" $argList
  61. }
  62.  
  63. # tkIconList_Create --
  64. #
  65. #    Creates an IconList widget by assembling a canvas widget and a
  66. #    scrollbar widget. Sets all the bindings necessary for the IconList's
  67. #    operations.
  68. #
  69. proc tkIconList_Create {w} {
  70.     upvar #0 $w data
  71.  
  72.     frame $w
  73.     set data(sbar)   [scrollbar $w.sbar -orient horizontal \
  74.     -highlightthickness 0 -takefocus 0]
  75.     set data(canvas) [canvas $w.canvas -bd 2 -relief sunken \
  76.     -width 400 -height 120 -takefocus 1]
  77.     pack $data(sbar) -side bottom -fill x -padx 2
  78.     pack $data(canvas) -expand yes -fill both
  79.  
  80.     $data(sbar) config -command "$data(canvas) xview"
  81.     $data(canvas) config -xscrollcommand "$data(sbar) set"
  82.  
  83.     # Initializes the max icon/text width and height and other variables
  84.     #
  85.     set data(maxIW) 1
  86.     set data(maxIH) 1
  87.     set data(maxTW) 1
  88.     set data(maxTH) 1
  89.     set data(numItems) 0
  90.     set data(curItem)  {}
  91.     set data(noScroll) 1
  92.  
  93.     # Creates the event bindings.
  94.     #
  95.     bind $data(canvas) <Configure> "tkIconList_Arrange $w"
  96.  
  97.     bind $data(canvas) <1>         "tkIconList_Btn1 $w %x %y"
  98.     bind $data(canvas) <B1-Motion> "tkIconList_Motion1 $w %x %y"
  99.     bind $data(canvas) <Shift-1>   "tkIconList_ShiftBtn1 $w %x %y"
  100.     bind $data(canvas) <Double-1>  "tkIconList_Double1 $w %x %y"
  101.     bind $data(canvas) <ButtonRelease-1> "tkCancelRepeat"
  102.     bind $data(canvas) <B1-Leave>  "tkIconList_Leave1 $w %x %y"
  103.     bind $data(canvas) <B1-Enter>  "tkCancelRepeat"
  104.  
  105.     bind $data(canvas) <Up>        "tkIconList_UpDown $w -1"
  106.     bind $data(canvas) <Down>      "tkIconList_UpDown $w  1"
  107.     bind $data(canvas) <Left>      "tkIconList_LeftRight $w -1"
  108.     bind $data(canvas) <Right>     "tkIconList_LeftRight $w  1"
  109.     bind $data(canvas) <Return>    "tkIconList_ReturnKey $w"
  110.     bind $data(canvas) <KeyPress>  "tkIconList_KeyPress $w %A"
  111.     bind $data(canvas) <Control-KeyPress> ";"
  112.     bind $data(canvas) <Alt-KeyPress>  ";"
  113.  
  114.     bind $data(canvas) <FocusIn>   "tkIconList_FocusIn $w"
  115.  
  116.     return $w
  117. }
  118.  
  119. # tkIconList_AutoScan --
  120. #
  121. # This procedure is invoked when the mouse leaves an entry window
  122. # with button 1 down.  It scrolls the window up, down, left, or
  123. # right, depending on where the mouse left the window, and reschedules
  124. # itself as an "after" command so that the window continues to scroll until
  125. # the mouse moves back into the window or the mouse button is released.
  126. #
  127. # Arguments:
  128. # w -        The IconList window.
  129. #
  130. proc tkIconList_AutoScan {w} {
  131.     upvar #0 $w data
  132.     global tkPriv
  133.  
  134.     if {![winfo exists $w]} return
  135.     set x $tkPriv(x)
  136.     set y $tkPriv(y)
  137.  
  138.     if $data(noScroll) {
  139.     return
  140.     }
  141.     if {$x >= [winfo width $data(canvas)]} {
  142.     $data(canvas) xview scroll 1 units
  143.     } elseif {$x < 0} {
  144.     $data(canvas) xview scroll -1 units
  145.     } elseif {$y >= [winfo height $data(canvas)]} {
  146.     # do nothing
  147.     } elseif {$y < 0} {
  148.     # do nothing
  149.     } else {
  150.     return
  151.     }
  152.  
  153.     tkIconList_Motion1 $w $x $y
  154.     set tkPriv(afterId) [after 50 tkIconList_AutoScan $w]
  155. }
  156.  
  157. # Deletes all the items inside the canvas subwidget and reset the IconList's
  158. # state.
  159. #
  160. proc tkIconList_DeleteAll {w} {
  161.     upvar #0 $w data
  162.     upvar #0 $w:itemList itemList
  163.  
  164.     $data(canvas) delete all
  165.     catch {unset data(selected)}
  166.     catch {unset data(rect)}
  167.     catch {unset data(list)}
  168.     catch {unset itemList}
  169.     set data(maxIW) 1
  170.     set data(maxIH) 1
  171.     set data(maxTW) 1
  172.     set data(maxTH) 1
  173.     set data(numItems) 0
  174.     set data(curItem)  {}
  175.     set data(noScroll) 1
  176.     $data(sbar) set 0.0 1.0
  177.     $data(canvas) xview moveto 0
  178. }
  179.  
  180. # Adds an icon into the IconList with the designated image and text
  181. #
  182. proc tkIconList_Add {w image text} {
  183.     upvar #0 $w data
  184.     upvar #0 $w:itemList itemList
  185.     upvar #0 $w:textList textList
  186.  
  187.     set iTag [$data(canvas) create image 0 0 -image $image -anchor nw]
  188.     set tTag [$data(canvas) create text  0 0 -text  $text  -anchor nw \
  189.     -font $data(font)]
  190.     set rTag [$data(canvas) create rect  0 0 0 0 -fill "" -outline ""]
  191.     
  192.     set b [$data(canvas) bbox $iTag]
  193.     set iW [expr [lindex $b 2]-[lindex $b 0]]
  194.     set iH [expr [lindex $b 3]-[lindex $b 1]]
  195.     if {$data(maxIW) < $iW} {
  196.     set data(maxIW) $iW
  197.     }
  198.     if {$data(maxIH) < $iH} {
  199.     set data(maxIH) $iH
  200.     }
  201.     
  202.     set b [$data(canvas) bbox $tTag]
  203.     set tW [expr [lindex $b 2]-[lindex $b 0]]
  204.     set tH [expr [lindex $b 3]-[lindex $b 1]]
  205.     if {$data(maxTW) < $tW} {
  206.     set data(maxTW) $tW
  207.     }
  208.     if {$data(maxTH) < $tH} {
  209.     set data(maxTH) $tH
  210.     }
  211.     
  212.     lappend data(list) [list $iTag $tTag $rTag $iW $iH $tW $tH $data(numItems)]
  213.     set itemList($rTag) [list $iTag $tTag $text $data(numItems)]
  214.     set textList($data(numItems)) [string tolower $text]
  215.     incr data(numItems)
  216. }
  217.  
  218. # Places the icons in a column-major arrangement.
  219. #
  220. proc tkIconList_Arrange {w} {
  221.     upvar #0 $w data
  222.  
  223.     if ![info exists data(list)] {
  224.     if {[info exists data(canvas)] && [winfo exists $data(canvas)]} {
  225.         set data(noScroll) 1
  226.         $data(sbar) config -command ""
  227.     }
  228.     return
  229.     }
  230.  
  231.     set W [winfo width  $data(canvas)]
  232.     set H [winfo height $data(canvas)]
  233.     set pad [expr [$data(canvas) cget -highlightthickness] + \
  234.     [$data(canvas) cget -bd]]
  235.     if {$pad < 2} {
  236.     set pad 2
  237.     }
  238.  
  239.     incr W -[expr $pad*2]
  240.     incr H -[expr $pad*2]
  241.  
  242.     set dx [expr $data(maxIW) + $data(maxTW) + 8]
  243.     if {$data(maxTH) > $data(maxIH)} {
  244.     set dy $data(maxTH)
  245.     } else {
  246.     set dy $data(maxIH)
  247.     }
  248.     incr dy 2
  249.     set shift [expr $data(maxIW) + 4]
  250.  
  251.     set x [expr $pad * 2]
  252.     set y [expr $pad * 1]
  253.     set usedColumn 0
  254.     foreach sublist $data(list) {
  255.     set usedColumn 1
  256.     set iTag [lindex $sublist 0]
  257.     set tTag [lindex $sublist 1]
  258.     set rTag [lindex $sublist 2]
  259.     set iW   [lindex $sublist 3]
  260.     set iH   [lindex $sublist 4]
  261.     set tW   [lindex $sublist 5]
  262.     set tH   [lindex $sublist 6]
  263.  
  264.     set i_dy [expr ($dy - $iH)/2]
  265.     set t_dy [expr ($dy - $tH)/2]
  266.  
  267.     $data(canvas) coords $iTag $x                 [expr $y + $i_dy]
  268.     $data(canvas) coords $tTag [expr $x + $shift] [expr $y + $t_dy]
  269.     $data(canvas) coords $tTag [expr $x + $shift] [expr $y + $t_dy]
  270.     $data(canvas) coords $rTag $x $y [expr $x+$dx] [expr $y+$dy]
  271.  
  272.     incr y $dy
  273.     if {[expr $y + $dy] > $H} {
  274.         set y [expr $pad * 1]
  275.         incr x $dx
  276.         set usedColumn 0
  277.     }
  278.     }
  279.  
  280.     if {$usedColumn} {
  281.     set sW [expr $x + $dx]
  282.     } else {
  283.     set sW $x
  284.     }
  285.  
  286.     if {$sW < $W} {
  287.     $data(canvas) config -scrollregion "$pad $pad $sW $H"
  288.     $data(sbar) config -command ""
  289.     $data(canvas) xview moveto 0
  290.     set data(noScroll) 1
  291.     } else {
  292.     $data(canvas) config -scrollregion "$pad $pad $sW $H"
  293.     $data(sbar) config -command "$data(canvas) xview"
  294.     set data(noScroll) 0
  295.     }
  296.  
  297.     set data(itemsPerColumn) [expr ($H-$pad)/$dy]
  298.     if {$data(itemsPerColumn) < 1} {
  299.     set data(itemsPerColumn) 1
  300.     }
  301.  
  302.     if {$data(curItem) != {}} {
  303.     tkIconList_Select $w [lindex [lindex $data(list) $data(curItem)] 2] 0
  304.     }
  305. }
  306.  
  307. # Gets called when the user invokes the IconList (usually by double-clicking
  308. # or pressing the Return key).
  309. #
  310. proc tkIconList_Invoke {w} {
  311.     upvar #0 $w data
  312.  
  313.     if {[string compare $data(-command) ""] && [info exists data(selected)]} {
  314.     eval $data(-command) [list $data(selected)]
  315.     }
  316. }
  317.  
  318. # tkIconList_See --
  319. #
  320. #    If the item is not (completely) visible, scroll the canvas so that
  321. #    it becomes visible.
  322. proc tkIconList_See {w rTag} {
  323.     upvar #0 $w data
  324.     upvar #0 $w:itemList itemList
  325.  
  326.     if $data(noScroll) {
  327.     return
  328.     }
  329.     set sRegion [$data(canvas) cget -scrollregion]
  330.     if ![string compare $sRegion {}] {
  331.     return
  332.     }
  333.  
  334.     if ![info exists itemList($rTag)] {
  335.     return
  336.     }
  337.  
  338.  
  339.     set bbox [$data(canvas) bbox $rTag]
  340.     set pad [expr [$data(canvas) cget -highlightthickness] + \
  341.     [$data(canvas) cget -bd]]
  342.  
  343.     set x1 [lindex $bbox 0]
  344.     set x2 [lindex $bbox 2]
  345.     incr x1 -[expr $pad * 2]
  346.     incr x2 -[expr $pad * 1]
  347.  
  348.     set cW [expr [winfo width $data(canvas)] - $pad*2]
  349.  
  350.     set scrollW [expr [lindex $sRegion 2]-[lindex $sRegion 0]+1]
  351.     set dispX [expr int([lindex [$data(canvas) xview] 0]*$scrollW)]
  352.     set oldDispX $dispX
  353.  
  354.     # check if out of the right edge
  355.     #
  356.     if {[expr $x2 - $dispX] >= $cW} {
  357.     set dispX [expr $x2 - $cW]
  358.     }
  359.     # check if out of the left edge
  360.     #
  361.     if {[expr $x1 - $dispX] < 0} {
  362.     set dispX $x1
  363.     }
  364.  
  365.     if {$oldDispX != $dispX} {
  366.     set fraction [expr double($dispX)/double($scrollW)]
  367.     $data(canvas) xview moveto $fraction
  368.     }
  369. }
  370.  
  371. proc tkIconList_SelectAtXY {w x y} {
  372.     upvar #0 $w data
  373.  
  374.     tkIconList_Select $w [$data(canvas) find closest \
  375.     [$data(canvas) canvasx $x] [$data(canvas) canvasy $y]]
  376. }
  377.  
  378. proc tkIconList_AddSelectAtXY {w x y {no_delete 0}} {
  379.     upvar #0 $w data
  380.  
  381.     if {$data(-multiple) && [info exists data(selected)]} {
  382.     tkIconList_AddSelect $w [$data(canvas) find closest \
  383.         [$data(canvas) canvasx $x] [$data(canvas) canvasy $y]] \
  384.         1 $no_delete
  385.     return
  386.     }
  387.     tkIconList_SelectAtXY $w $x $y
  388. }
  389.  
  390. proc tkIconList_Select {w rTag {callBrowse 1}} {
  391.     upvar #0 $w data
  392.     upvar #0 $w:itemList itemList
  393.  
  394.     if ![info exists itemList($rTag)] {
  395.     return
  396.     }
  397.     set iTag   [lindex $itemList($rTag) 0]
  398.     set tTag   [lindex $itemList($rTag) 1]
  399.     set text   [lindex $itemList($rTag) 2]
  400.     set serial [lindex $itemList($rTag) 3]
  401.  
  402.     if {$data(-multiple) && [info exists data(rect)]} {
  403.     foreach r $data(rect) {
  404.         $data(canvas) delete $r
  405.     }
  406.     unset data(rect)
  407.     }
  408.     if ![info exists data(rect)] {
  409.         set data(rect) [$data(canvas) create rect 0 0 0 0 \
  410.         -fill #a0a0ff -outline #a0a0ff]
  411.     }
  412.     $data(canvas) lower $data(rect)
  413.     set bbox [$data(canvas) bbox $tTag]
  414.     eval $data(canvas) coords $data(rect) $bbox
  415.  
  416.     set data(curItem) $serial
  417.     
  418.     #we can't set the text to data(selected) as text, this is bugy,
  419.     #when the path contains blanks
  420.     if {$data(-multiple)} {
  421.     catch {unset data(selected)}
  422.     lappend data(selected) $text
  423.     } else {
  424.         set data(selected) $text
  425.     }
  426.     
  427.     if {$callBrowse} {
  428.     if [string compare $data(-browsecmd) ""] {
  429.         eval $data(-browsecmd) [list $data(selected)]
  430.     }
  431.     }
  432. }
  433.  
  434. proc tkIconList_AddSelect {w rTag {callBrowse 1} {no_delete 0}} {
  435.     upvar #0 $w data
  436.     upvar #0 $w:itemList itemList
  437.  
  438.     if ![info exists itemList($rTag)] {
  439.     return
  440.     }
  441.     set iTag   [lindex $itemList($rTag) 0]
  442.     set tTag   [lindex $itemList($rTag) 1]
  443.     set text   [lindex $itemList($rTag) 2]
  444.     set serial [lindex $itemList($rTag) 3]
  445.  
  446.     if {[lsearch -exact $data(selected) $text] != -1} {
  447.     if {$no_delete} {
  448.         return
  449.     }
  450.      
  451.     # we've clicked on an existing item, so we need to remove it
  452.     set i [lsearch -exact $data(selected) $text]
  453.     set data(selected) [lreplace $data(selected) $i $i]
  454.     
  455.     # find the appropriate coordinates and remove the
  456.     # corresponding rectangle.
  457.     set tmpbbox [$data(canvas) bbox $tTag]
  458.     for {set i 0} {$i<[llength $data(rect)]} {incr i} {
  459.         set rectTag [lindex $data(rect) $i]
  460.         set testbbox [$data(canvas) coords $rectTag]
  461.         # test first two coordinates; if they're the same the
  462.         # entire box should match
  463.         if {[lindex $testbbox 0]==[lindex $tmpbbox 0] && \
  464.             [lindex $testbbox 1]==[lindex $tmpbbox 1]} {
  465.         $data(canvas) delete $rectTag
  466.         set data(rect) [lreplace $data(rect) $i $i]
  467.         break
  468.         }
  469.     }
  470.  
  471.     if {$callBrowse} {
  472.         if [string compare $data(-browsecmd) ""] {
  473.         eval $data(-browsecmd) [list $data(selected)]
  474.         }
  475.     }
  476.     return
  477.     }
  478.  
  479.     set tmprect [$data(canvas) create rect 0 0 0 0 \
  480.         -fill #a0a0ff -outline #a0a0ff]
  481.     lappend data(rect) $tmprect
  482.  
  483.     $data(canvas) lower $tmprect
  484.     set bbox [$data(canvas) bbox $tTag]
  485.     eval $data(canvas) coords $tmprect $bbox
  486.  
  487.     set data(curItem) $serial
  488.     lappend data(selected) $text
  489.     
  490.     if {$callBrowse} {
  491.     if [string compare $data(-browsecmd) ""] {
  492.         eval $data(-browsecmd) [list $data(selected)]
  493.     }
  494.     }
  495. }
  496.  
  497. proc tkIconList_Unselect {w} {
  498.     upvar #0 $w data
  499.  
  500.     if [info exists data(rect)] {
  501.     foreach r $data(rect) {
  502.         $data(canvas) delete $r
  503.     }
  504.     unset data(rect)
  505.     }
  506.     if [info exists data(selected)] {
  507.     unset data(selected)
  508.     }
  509.     set data(curItem)  {}
  510. }
  511.  
  512. # Returns the selected item
  513. #
  514. proc tkIconList_Get {w} {
  515.     upvar #0 $w data
  516.  
  517.     if [info exists data(selected)] {
  518.     return $data(selected)
  519.     } else {
  520.     return ""
  521.     }
  522. }
  523.  
  524.  
  525. proc tkIconList_Btn1 {w x y} {
  526.     upvar #0 $w data
  527.  
  528.     focus $data(canvas)
  529.     tkIconList_SelectAtXY $w $x $y
  530. }
  531.  
  532. proc tkIconList_ShiftBtn1 {w x y} {
  533.     upvar #0 $w data
  534.  
  535.     focus $data(canvas)
  536.     tkIconList_AddSelectAtXY $w $x $y
  537. }
  538.  
  539. # Gets called on button-1 motions
  540. #
  541. proc tkIconList_Motion1 {w x y} {
  542.     global tkPriv
  543.     set tkPriv(x) $x
  544.     set tkPriv(y) $y
  545.  
  546.     tkIconList_AddSelectAtXY $w $x $y 1
  547. }
  548.  
  549. proc tkIconList_Double1 {w x y} {
  550.     upvar #0 $w data
  551.  
  552.     if {$data(curItem) != {}} {
  553.     tkIconList_Invoke $w
  554.     }
  555. }
  556.  
  557. proc tkIconList_ReturnKey {w} {
  558.     tkIconList_Invoke $w
  559. }
  560.  
  561. proc tkIconList_Leave1 {w x y} {
  562.     global tkPriv
  563.  
  564.     set tkPriv(x) $x
  565.     set tkPriv(y) $y
  566.     tkIconList_AutoScan $w
  567. }
  568.  
  569. proc tkIconList_FocusIn {w} {
  570.     upvar #0 $w data
  571.  
  572.     if ![info exists data(list)] {
  573.     return
  574.     }
  575.  
  576.     if {$data(curItem) == {}} {
  577.     set rTag [lindex [lindex $data(list) 0] 2]
  578.     tkIconList_Select $w $rTag
  579.     }
  580. }
  581.  
  582. # tkIconList_UpDown --
  583. #
  584. # Moves the active element up or down by one element
  585. #
  586. # Arguments:
  587. # w -        The IconList widget.
  588. # amount -    +1 to move down one item, -1 to move back one item.
  589. #
  590. proc tkIconList_UpDown {w amount} {
  591.     upvar #0 $w data
  592.  
  593.     if ![info exists data(list)] {
  594.     return
  595.     }
  596.  
  597.     if {$data(curItem) == {}} {
  598.     set rTag [lindex [lindex $data(list) 0] 2]
  599.     } else {
  600.     set oldRTag [lindex [lindex $data(list) $data(curItem)] 2]
  601.     set rTag [lindex [lindex $data(list) [expr $data(curItem)+$amount]] 2]
  602.     if ![string compare $rTag ""] {
  603.         set rTag $oldRTag
  604.     }
  605.     }
  606.  
  607.     if [string compare $rTag ""] {
  608.     tkIconList_Select $w $rTag
  609.     tkIconList_See $w $rTag
  610.     }
  611. }
  612.  
  613. # tkIconList_LeftRight --
  614. #
  615. # Moves the active element left or right by one column
  616. #
  617. # Arguments:
  618. # w -        The IconList widget.
  619. # amount -    +1 to move right one column, -1 to move left one column.
  620. #
  621. proc tkIconList_LeftRight {w amount} {
  622.     upvar #0 $w data
  623.  
  624.     if ![info exists data(list)] {
  625.     return
  626.     }
  627.     if {$data(curItem) == {}} {
  628.     set rTag [lindex [lindex $data(list) 0] 2]
  629.     } else {
  630.     set oldRTag [lindex [lindex $data(list) $data(curItem)] 2]
  631.     set newItem [expr $data(curItem)+($amount*$data(itemsPerColumn))]
  632.     set rTag [lindex [lindex $data(list) $newItem] 2]
  633.     if ![string compare $rTag ""] {
  634.         set rTag $oldRTag
  635.     }
  636.     }
  637.  
  638.     if [string compare $rTag ""] {
  639.     tkIconList_Select $w $rTag
  640.     tkIconList_See $w $rTag
  641.     }
  642. }
  643.  
  644. #----------------------------------------------------------------------
  645. #        Accelerator key bindings
  646. #----------------------------------------------------------------------
  647.  
  648. # tkIconList_KeyPress --
  649. #
  650. #    Gets called when user enters an arbitrary key in the listbox.
  651. #
  652. proc tkIconList_KeyPress {w key} {
  653.     global tkPriv
  654.  
  655.     append tkPriv(ILAccel,$w) $key
  656.     tkIconList_Goto $w $tkPriv(ILAccel,$w)
  657.     catch {
  658.     after cancel $tkPriv(ILAccel,$w,afterId)
  659.     }
  660.     set tkPriv(ILAccel,$w,afterId) [after 500 tkIconList_Reset $w]
  661. }
  662.  
  663. proc tkIconList_Goto {w text} {
  664.     upvar #0 $w data
  665.     upvar #0 $w:textList textList
  666.     global tkPriv
  667.     
  668.     if ![info exists data(list)] {
  669.     return
  670.     }
  671.  
  672.     if {[string length $text] == 0} {
  673.     return
  674.     }
  675.  
  676.     if {$data(curItem) == {} || $data(curItem) == 0} {
  677.     set start  0
  678.     } else {
  679.     set start  $data(curItem)
  680.     }
  681.  
  682.     set text [string tolower $text]
  683.     set theIndex -1
  684.     set less 0
  685.     set len [string length $text]
  686.     set len0 [expr $len-1]
  687.     set i $start
  688.  
  689.     # Search forward until we find a filename whose prefix is an exact match
  690.     # with $text
  691.     while 1 {
  692.     set sub [string range $textList($i) 0 $len0]
  693.     if {[string compare $text $sub] == 0} {
  694.         set theIndex $i
  695.         break
  696.     }
  697.     incr i
  698.     if {$i == $data(numItems)} {
  699.         set i 0
  700.     }
  701.     if {$i == $start} {
  702.         break
  703.     }
  704.     }
  705.  
  706.     if {$theIndex > -1} {
  707.     set rTag [lindex [lindex $data(list) $theIndex] 2]
  708.     tkIconList_Select $w $rTag 0
  709.     tkIconList_See $w $rTag
  710.     }
  711. }
  712.  
  713. proc tkIconList_Reset {w} {
  714.     global tkPriv
  715.  
  716.     catch {unset tkPriv(ILAccel,$w)}
  717. }
  718.  
  719. #----------------------------------------------------------------------
  720. #
  721. #              F I L E   D I A L O G
  722. #
  723. #----------------------------------------------------------------------
  724.  
  725. # tkFDialog --
  726. #
  727. #    Implements the TK file selection dialog. This dialog is used when
  728. #    the tk_strictMotif flag is set to false. This procedure shouldn't
  729. #    be called directly. Call tk_getOpenFile or tk_getSaveFile instead.
  730. #
  731. proc tkFDialog {args} {
  732.     global tkPriv
  733.     global __old_dialog
  734.     global __old_multiple
  735.     set w __tk_filedialog
  736.     upvar #0 $w data
  737.  
  738.     if ![string compare [lindex [info level 0] 0] tk_getOpenFile] {
  739.     set type open
  740.     } else {
  741.     set type save
  742.     }
  743.  
  744.     tkFDialog_Config $w $type $args
  745.  
  746.     if {![string compare $data(-parent) .]} {
  747.         set w .$w
  748.     } else {
  749.         set w $data(-parent).$w
  750.     }
  751.  
  752.     #because tk doesn't use window-path dependent array, it is
  753.     #impossible to use more than one dialog box at the same time,
  754.     #so we have to recreate the dialog!
  755.     if {[info exists __old_dialog] \
  756.     && ($__old_dialog != $w || $__old_multiple != $data(-multiple))} {
  757.         catch {destroy $w}
  758.     catch {destroy $__old_dialog}
  759.     }
  760.     set __old_dialog $w
  761.     set __old_multiple $data(-multiple)
  762.  
  763.     # (re)create the dialog box if necessary
  764.     #
  765.     set new_dialog 0
  766.     if {![winfo exists $w]} {
  767.     tkFDialog_Create $w
  768.     set new_dialog 1
  769.     } elseif {[string compare [winfo class $w] TkFDialog]} {
  770.     destroy $w
  771.     tkFDialog_Create $w
  772.     set new_dialog 1
  773.     }
  774.     wm transient $w $data(-parent)
  775.     #trace variable
  776.     trace variable data(selectPath) w "tkFDialog_SetPath $w"
  777.  
  778.     # 5. Initialize the file types menu
  779.     #
  780.     if {$data(-filetypes) != {}} {
  781.     $data(typeMenu) delete 0 end
  782.     foreach type $data(-filetypes) {
  783.         set title  [lindex $type 0]
  784.         set filter [lindex $type 1]
  785.         $data(typeMenu) add command -label $title \
  786.         -command [list tkFDialog_SetFilter $w $type]
  787.     }
  788.     tkFDialog_SetFilter $w [lindex $data(-filetypes) 0]
  789.     $data(typeMenuBtn) config -state normal
  790.     $data(typeMenuLab) config -state normal
  791.     } else {
  792.     set data(filter) "*"
  793.     $data(typeMenuBtn) config -state disabled -takefocus 0
  794.     $data(typeMenuLab) config -state disabled
  795.     }
  796.  
  797.     tkFDialog_UpdateWhenIdle $w
  798.  
  799.     # 6. Withdraw the window, then update all the geometry information
  800.     # so we know how big it wants to be, then center the window in the
  801.     # display and de-iconify it.
  802.  
  803.     if {$new_dialog} {
  804.         #center dialog, when it has been new created
  805.     wm withdraw $w
  806.     update idletasks
  807.     set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
  808.         - [winfo vrootx [winfo parent $w]]]
  809.     set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
  810.         - [winfo vrooty [winfo parent $w]]]
  811.     wm geom $w [winfo reqwidth $w]x[winfo reqheight $w]+$x+$y
  812.     }
  813.     wm title $w $data(-title)
  814.     wm deiconify $w
  815.  
  816.     # 7. Set a grab and claim the focus too.
  817.  
  818.     set oldFocus [focus]
  819.     set oldGrab [grab current $w]
  820.     if {$oldGrab != ""} {
  821.     set grabStatus [grab status $oldGrab]
  822.     }
  823.     grab $w
  824.     focus $data(ent)
  825.     $data(ent) delete 0 end
  826.     $data(ent) insert 0 $data(selectFile)
  827.     $data(ent) select from 0
  828.     $data(ent) select to   end
  829.     $data(ent) icursor end
  830.  
  831.     # 8. Wait for the user to respond, then restore the focus and
  832.     # return the index of the selected button.  Restore the focus
  833.     # before deleting the window, since otherwise the window manager
  834.     # may take the focus away so we can't redirect it.  Finally,
  835.     # restore any grab that was in effect.
  836.  
  837.     tkwait variable tkPriv(selectFilePath)
  838.     catch {focus $oldFocus}
  839.     grab release $w
  840.     wm withdraw $w
  841.     if {$oldGrab != ""} {
  842.     if {$grabStatus == "global"} {
  843.         grab -global $oldGrab
  844.     } else {
  845.         grab $oldGrab
  846.     }
  847.     }
  848.     #delete the tracer, because this conflicts with multiple
  849.     #used dialogs
  850.     trace vdelete data(selectPath) w "tkFDialog_SetPath $w"
  851.     return $tkPriv(selectFilePath)
  852. }
  853.  
  854. # tkFDialog_Config --
  855. #
  856. #    Configures the TK filedialog according to the argument list
  857. #
  858. proc tkFDialog_Config {w type argList} {
  859.     upvar #0 $w data
  860.  
  861.     set data(type) $type
  862.  
  863.     # 1: the configuration specs
  864.     #
  865.     set specs {
  866.     {-defaultextension "" "" ""}
  867.     {-filetypes "" "" ""}
  868.     {-initialdir "" "" ""}
  869.     {-initialfile "" "" ""}
  870.     {-parent "" "" "."}
  871.     {-title "" "" ""}
  872.     }
  873.     if ![string compare $type open] {
  874.         # CYGNUS LOCAL: Handle -choosedir.
  875.         # Note: the -choosedir option is a Cygnus extension.  It is not
  876.         # documented since it only works on Unix -- it is an
  877.         # implementation detail of the directory-choosing code in
  878.         # in libgui.
  879.     lappend specs {-multiple "" "" "0"} {-choosedir "" "" "0"}
  880.     # END CYGNUS LOCAL
  881.     }
  882.  
  883.     # 2: default values depending on the type of the dialog
  884.     #
  885.     if ![info exists data(selectPath)] {
  886.     # first time the dialog has been popped up
  887.     set data(selectPath) [pwd]
  888.     set data(selectFile) ""
  889.     }
  890.  
  891.     # 3: parse the arguments
  892.     #
  893.     tclParseConfigSpec $w $specs "" $argList
  894.  
  895.     if ![string compare $data(-title) ""] {
  896.     if ![string compare $type "open"] {
  897.         set data(-title) "Open"
  898.     } else {
  899.         set data(-title) "Save As"
  900.     }
  901.     }
  902.  
  903.     # 4: set the default directory and selection according to the -initial
  904.     #    settings
  905.     #
  906.     # Khamis 16-04-98
  907.     # When the path contains blanks, glob returns an item in a list, but
  908.     # data(selectPath) must be an item and not a list of items, so we
  909.     # must extract the item from the returned list.
  910.     if [string compare $data(-initialdir) ""] {
  911.     if [file isdirectory $data(-initialdir)] {
  912.         #khamis: Join result of glob to an item
  913.         set data(selectPath) [lindex [glob $data(-initialdir)] 0]
  914.     } else {
  915.         error "\"$data(-initialdir)\" is not a valid directory"
  916.     }
  917.     }
  918.     set data(selectFile) $data(-initialfile)
  919.  
  920.     # 5. Parse the -filetypes option
  921.     #
  922.     set data(-filetypes) [tkFDGetFileTypes $data(-filetypes)]
  923.  
  924.     if ![winfo exists $data(-parent)] {
  925.     error "bad window path name \"$data(-parent)\""
  926.     }
  927.  
  928.     # Set -multiple to a one or zero value (not other boolean types
  929.     # like "yes") so we can use it in tests easier.
  930.     if ![string compare $type save] {
  931.     set data(-multiple) 0
  932.         # CYGNUS LOCAL: choosedir
  933.         # Handle -choosedir here as well.
  934.         set data(-choosedir) 0
  935.         # END CYGNUS LOCAL
  936.     } else {
  937.     if {$data(-multiple)} { 
  938.         set data(-multiple) 1 
  939.     }
  940.     }
  941. }
  942.  
  943. proc tkFDialog_Create {w} {
  944.     set dataName [lindex [split $w .] end]
  945.     upvar #0 $dataName data
  946.     global tk_library
  947.  
  948.     toplevel $w -class TkFDialog
  949.  
  950.     # f1: the frame with the directory option menu
  951.     #
  952.     set f1 [frame $w.f1]
  953.     label $f1.lab -text "Directory:" -under 0
  954.     set data(dirMenuBtn) $f1.menu
  955.     set data(dirMenu) [tk_optionMenu $f1.menu [format %s(selectPath) $dataName] ""]
  956.     set data(upBtn) [button $f1.up]
  957.     if ![info exists tkPriv(updirImage)] {
  958.     set tkPriv(updirImage) [image create bitmap -data {
  959. #define updir_width 28
  960. #define updir_height 16
  961. static char updir_bits[] = {
  962.    0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00,
  963.    0x20, 0x40, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x10, 0x00, 0x00, 0x01,
  964.    0x10, 0x02, 0x00, 0x01, 0x10, 0x07, 0x00, 0x01, 0x90, 0x0f, 0x00, 0x01,
  965.    0x10, 0x02, 0x00, 0x01, 0x10, 0x02, 0x00, 0x01, 0x10, 0x02, 0x00, 0x01,
  966.    0x10, 0xfe, 0x07, 0x01, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01,
  967.    0xf0, 0xff, 0xff, 0x01};}]
  968.     }
  969.     $data(upBtn) config -image $tkPriv(updirImage)
  970.  
  971.     $f1.menu config -takefocus 1 -highlightthickness 2
  972.  
  973.     pack $data(upBtn) -side right -padx 4 -fill both
  974.     pack $f1.lab -side left -padx 4 -fill both
  975.     pack $f1.menu -expand yes -fill both -padx 4
  976.  
  977.     # data(icons): the IconList that list the files and directories.
  978.     #
  979.     set data(icons) [tkIconList $w.icons \
  980.     -browsecmd "tkFDialog_ListBrowse $w" \
  981.     -command   "tkFDialog_ListInvoke $w" \
  982.     -multiple  "$data(-multiple)"]
  983.  
  984.     # f2: the frame with the OK button and the "file name" field
  985.     #
  986.     set f2 [frame $w.f2 -bd 0]
  987.     label $f2.lab -text "File name:" -anchor e -width 14 -under 5 -pady 0
  988.     if {$data(-multiple)} {
  989.     $f2.lab config -text "File names:"
  990.     }
  991.     set data(ent) [entry $f2.ent]
  992.  
  993.     # The font to use for the icons. The default Canvas font on Unix
  994.     # is just deviant.
  995.     global $w.icons
  996.     set $w.icons(font) [$data(ent) cget -font]
  997.  
  998.     # f3: the frame with the cancel button and the file types field
  999.     #
  1000.     set f3 [frame $w.f3 -bd 0]
  1001.  
  1002.     # The "File of types:" label needs to be grayed-out when
  1003.     # -filetypes are not specified. The label widget does not support
  1004.     # grayed-out text on monochrome displays. Therefore, we have to
  1005.     # use a button widget to emulate a label widget (by setting its
  1006.     # bindtags)
  1007.  
  1008.     set data(typeMenuLab) [button $f3.lab -text "Files of type:" \
  1009.     -anchor e -width 14 -under 9 \
  1010.     -bd [$f2.lab cget -bd] \
  1011.     -highlightthickness [$f2.lab cget -highlightthickness] \
  1012.     -relief [$f2.lab cget -relief] \
  1013.     -padx [$f2.lab cget -padx] \
  1014.     -pady [$f2.lab cget -pady]]
  1015.     bindtags $data(typeMenuLab) [list $data(typeMenuLab) Label \
  1016.         [winfo toplevel $data(typeMenuLab)] all]
  1017.  
  1018.     set data(typeMenuBtn) [menubutton $f3.menu -indicatoron 1 -menu $f3.menu.m]
  1019.     set data(typeMenu) [menu $data(typeMenuBtn).m -tearoff 0]
  1020.     $data(typeMenuBtn) config -takefocus 1 -highlightthickness 2 \
  1021.     -relief raised -bd 2 -anchor w
  1022.  
  1023.     # the okBtn is created after the typeMenu so that the keyboard traversal
  1024.     # is in the right order
  1025.     set data(okBtn)     [button $f2.ok     -text OK     -under 0 -width 6 \
  1026.     -default active -pady 3]
  1027.     set data(cancelBtn) [button $f3.cancel -text Cancel -under 0 -width 6\
  1028.     -default normal -pady 3]
  1029.  
  1030.     # pack the widgets in f2 and f3
  1031.     #
  1032.     pack $data(okBtn) -side right -padx 4 -anchor e
  1033.     pack $f2.lab -side left -padx 4
  1034.     pack $f2.ent -expand yes -fill x -padx 2 -pady 0
  1035.     
  1036.     pack $data(cancelBtn) -side right -padx 4 -anchor w
  1037.     pack $data(typeMenuLab) -side left -padx 4
  1038.     pack $data(typeMenuBtn) -expand yes -fill x -side right
  1039.  
  1040.     # Pack all the frames together. We are done with widget construction.
  1041.     #
  1042.     pack $f1 -side top -fill x -pady 4
  1043.     pack $f3 -side bottom -fill x
  1044.     pack $f2 -side bottom -fill x
  1045.     pack $data(icons) -expand yes -fill both -padx 4 -pady 1
  1046.  
  1047.     # Set up the event handlers
  1048.     #
  1049.     bind $data(ent) <Return>  "tkFDialog_ActivateEnt $w"
  1050.     
  1051.     $data(upBtn)     config -command "tkFDialog_UpDirCmd $w"
  1052.     $data(okBtn)     config -command "tkFDialog_OkCmd $w"
  1053.     $data(cancelBtn) config -command "tkFDialog_CancelCmd $w"
  1054.  
  1055.     #trace variable data(selectPath) w "tkFDialog_SetPath $w"
  1056.  
  1057.     bind $w <Alt-d> "focus $data(dirMenuBtn)"
  1058.     bind $w <Alt-t> [format {
  1059.     if {"[%s cget -state]" == "normal"} {
  1060.         focus %s
  1061.     }
  1062.     } $data(typeMenuBtn) $data(typeMenuBtn)]
  1063.     bind $w <Alt-n> "focus $data(ent)"
  1064.     bind $w <KeyPress-Escape> "tkButtonInvoke $data(cancelBtn)"
  1065.     bind $w <Alt-c> "tkButtonInvoke $data(cancelBtn)"
  1066.     bind $w <Alt-o> "tkFDialog_InvokeBtn $w Open"
  1067.     bind $w <Alt-s> "tkFDialog_InvokeBtn $w Save"
  1068.  
  1069.     wm protocol $w WM_DELETE_WINDOW "tkFDialog_CancelCmd $w"
  1070.  
  1071.     # Build the focus group for all the entries
  1072.     #
  1073.     tkFocusGroup_Create $w
  1074.     tkFocusGroup_BindIn $w  $data(ent) "tkFDialog_EntFocusIn $w"
  1075.     tkFocusGroup_BindOut $w $data(ent) "tkFDialog_EntFocusOut $w"
  1076. }
  1077.  
  1078. # tkFDialog_UpdateWhenIdle --
  1079. #
  1080. #    Creates an idle event handler which updates the dialog in idle
  1081. #    time. This is important because loading the directory may take a long
  1082. #    time and we don't want to load the same directory for multiple times
  1083. #    due to multiple concurrent events.
  1084. #
  1085. proc tkFDialog_UpdateWhenIdle {w} {
  1086.     upvar #0 [winfo name $w] data
  1087.  
  1088.     if [info exists data(updateId)] {
  1089.     return
  1090.     } else {
  1091.     set data(updateId) [after idle tkFDialog_Update $w]
  1092.     }
  1093. }
  1094.  
  1095. # tkFDialog_Update --
  1096. #
  1097. #    Loads the files and directories into the IconList widget. Also
  1098. #    sets up the directory option menu for quick access to parent
  1099. #    directories.
  1100. #
  1101. proc tkFDialog_Update {w} {
  1102.     set dataName [winfo name $w]
  1103.     upvar #0 $dataName data
  1104.     global tk_library tkPriv
  1105.  
  1106.     # This proc may be called within an idle handler. Make sure that the
  1107.     # window has not been destroyed before this proc is called
  1108.     if {![winfo exists $w] || [string compare [winfo class $w] TkFDialog]} {
  1109.     return
  1110.     } else {
  1111.     catch {unset data(updateId)}
  1112.     }
  1113.  
  1114.     if ![info exists tkPriv(folderImage)] {
  1115.     set tkPriv(folderImage) [image create photo -data {
  1116. R0lGODlhEAAMAKEAAAD//wAAAPD/gAAAACH5BAEAAAAALAAAAAAQAAwAAAIghINhyycvVFsB
  1117. QtmS3rjaH1Hg141WaT5ouprt2HHcUgAAOw==}]
  1118.     set tkPriv(fileImage)   [image create photo -data {
  1119. R0lGODlhDAAMAKEAALLA3AAAAP//8wAAACH5BAEAAAAALAAAAAAMAAwAAAIgRI4Ha+IfWHsO
  1120. rSASvJTGhnhcV3EJlo3kh53ltF5nAhQAOw==}]
  1121.     }
  1122.     set folder $tkPriv(folderImage)
  1123.     set file   $tkPriv(fileImage)
  1124.  
  1125.     set appPWD [pwd]
  1126.     if [catch {
  1127.     cd $data(selectPath)
  1128.     }] {
  1129.     # We cannot change directory to $data(selectPath). $data(selectPath)
  1130.     # should have been checked before tkFDialog_Update is called, so
  1131.     # we normally won't come to here. Anyways, give an error and abort
  1132.     # action.
  1133.     tk_messageBox -type ok -parent $data(-parent) -message \
  1134.         "Cannot change to the directory \"$data(selectPath)\".\nPermission denied."\
  1135.         -icon warning
  1136.     cd $appPWD
  1137.     return
  1138.     }
  1139.  
  1140.     # Turn on the busy cursor. BUG?? We haven't disabled X events, though,
  1141.     # so the user may still click and cause havoc ...
  1142.     #
  1143.     set entCursor [$data(ent) cget -cursor]
  1144.     set dlgCursor [$w         cget -cursor]
  1145.     $data(ent) config -cursor watch
  1146.     $w         config -cursor watch
  1147.     update idletasks
  1148.     
  1149.     tkIconList_DeleteAll $data(icons)
  1150.  
  1151.     # Make the dir list
  1152.     #
  1153.     foreach f [lsort -dictionary [glob -nocomplain .* *]] {
  1154.     if ![string compare $f .] {
  1155.         continue
  1156.     }
  1157.     if ![string compare $f ..] {
  1158.         continue
  1159.     }
  1160.     if [file isdir ./$f] {
  1161.         if ![info exists hasDoneDir($f)] {
  1162.         tkIconList_Add $data(icons) $folder $f
  1163.         set hasDoneDir($f) 1
  1164.         }
  1165.     }
  1166.     }
  1167.     # Make the file list
  1168.     #
  1169.     if ![string compare $data(filter) *] {
  1170.     set files [lsort -dictionary \
  1171.         [glob -nocomplain .* *]]
  1172.     } else {
  1173.     set files [lsort -dictionary \
  1174.         [eval glob -nocomplain $data(filter)]]
  1175.     }
  1176.  
  1177.     set top 0
  1178.     foreach f $files {
  1179.     if ![file isdir ./$f] {
  1180.         if ![info exists hasDoneFile($f)] {
  1181.         tkIconList_Add $data(icons) $file $f
  1182.         set hasDoneFile($f) 1
  1183.         }
  1184.     }
  1185.     }
  1186.  
  1187.     tkIconList_Arrange $data(icons)
  1188.  
  1189.     # Update the Directory: option menu
  1190.     #
  1191.     set list ""
  1192.     set dir ""
  1193.     foreach subdir [file split $data(selectPath)] {
  1194.     set dir [file join $dir $subdir]
  1195.     lappend list $dir
  1196.     }
  1197.  
  1198.     $data(dirMenu) delete 0 end
  1199.     set var [format %s(selectPath) $dataName]
  1200.     foreach path $list {
  1201.     $data(dirMenu) add command -label $path -command [list set $var $path]
  1202.     }
  1203.  
  1204.     # Restore the PWD to the application's PWD
  1205.     #
  1206.     cd $appPWD
  1207.  
  1208.     # turn off the busy cursor.
  1209.     #
  1210.     $data(ent) config -cursor $entCursor
  1211.     $w         config -cursor $dlgCursor
  1212. }
  1213.  
  1214. # tkFDialog_SetPathSilently --
  1215. #
  1216. #     Sets data(selectPath) without invoking the trace procedure
  1217. #
  1218. proc tkFDialog_SetPathSilently {w path} {
  1219.     upvar #0 [winfo name $w] data
  1220.  
  1221.     trace vdelete  data(selectPath) w "tkFDialog_SetPath $w"
  1222.     set data(selectPath) $path
  1223.     trace variable data(selectPath) w "tkFDialog_SetPath $w"
  1224. }
  1225.  
  1226.  
  1227. # This proc gets called whenever data(selectPath) is set
  1228. #
  1229. proc tkFDialog_SetPath {w name1 name2 op} {
  1230.     if {![winfo exists $w]} {
  1231.         return
  1232.     }
  1233.     upvar #0 [winfo name $w] data
  1234.     tkFDialog_UpdateWhenIdle $w
  1235. }
  1236.  
  1237. # This proc gets called whenever data(filter) is set
  1238. #
  1239. proc tkFDialog_SetFilter {w type} {
  1240.     upvar #0 [winfo name $w] data
  1241.     upvar \#0 $data(icons) icons
  1242.  
  1243.     set data(filter) [lindex $type 1]
  1244.     $data(typeMenuBtn) config -text [lindex $type 0] -indicatoron 1
  1245.  
  1246.     $icons(sbar) set 0.0 0.0
  1247.     
  1248.     tkFDialog_UpdateWhenIdle $w
  1249. }
  1250.  
  1251. # tkFDialogResolveFile --
  1252. #
  1253. #    Interpret the user's text input in a file selection dialog.
  1254. #    Performs:
  1255. #
  1256. #    (1) ~ substitution
  1257. #    (2) resolve all instances of . and ..
  1258. #    (3) check for non-existent files/directories
  1259. #    (4) check for chdir permissions
  1260. #
  1261. # Arguments:
  1262. #    context:  the current directory you are in
  1263. #    text:      the text entered by the user
  1264. #    defaultext: the default extension to add to files with no extension
  1265. #
  1266. # Return vaue:
  1267. #    [list $flag $directory $file]
  1268. #
  1269. #     flag = OK    : valid input
  1270. #          = PATTERN    : valid directory/pattern
  1271. #          = PATH    : the directory does not exist
  1272. #          = FILE    : the directory exists by the file doesn't
  1273. #              exist
  1274. #          = CHDIR    : Cannot change to the directory
  1275. #          = ERROR    : Invalid entry
  1276. #
  1277. #     directory      : valid only if flag = OK or PATTERN or FILE
  1278. #     file           : valid only if flag = OK or PATTERN
  1279. #
  1280. #    directory may not be the same as context, because text may contain
  1281. #    a subdirectory name
  1282. #
  1283. proc tkFDialogResolveFile {context text defaultext} {
  1284.  
  1285.     set appPWD [pwd]
  1286.  
  1287.     set path [tkFDialog_JoinFile $context $text]
  1288.  
  1289.     if {[file ext $path] == ""} {
  1290.     set path "$path$defaultext"
  1291.     }
  1292.  
  1293.     if [catch {file exists $path}] {
  1294.     return [list ERROR $path ""]
  1295.     }
  1296.  
  1297.     if [catch {if [file exists $path] {}}] {
  1298.     # This "if" block can be safely removed if the following code returns
  1299.     # an error. It currently (7/22/97) doesn't
  1300.     #
  1301.     #    file exists ~nonsuchuser
  1302.     #
  1303.     return [list ERROR $path ""]
  1304.     }
  1305.  
  1306.     if [file exists $path] {
  1307.     if [file isdirectory $path] {
  1308.         if [catch {
  1309.         cd $path
  1310.         }] {
  1311.         return [list CHDIR $path ""]
  1312.         }
  1313.         set directory [pwd]
  1314.         set file ""
  1315.         set flag OK
  1316.         cd $appPWD
  1317.     } else {
  1318.         if [catch {
  1319.         cd [file dirname $path]
  1320.         }] {
  1321.         return [list CHDIR [file dirname $path] ""]
  1322.         }
  1323.         set directory [pwd]
  1324.         set file [file tail $path]
  1325.         set flag OK
  1326.         cd $appPWD
  1327.     }
  1328.     } else {
  1329.     set dirname [file dirname $path]
  1330.     if [file exists $dirname] {
  1331.         if [catch {
  1332.         cd $dirname
  1333.         }] {
  1334.         return [list CHDIR $dirname ""]
  1335.         }
  1336.         set directory [pwd]
  1337.         set file [file tail $path]
  1338.         if [regexp {[*]|[?]} $file] {
  1339.         set flag PATTERN
  1340.         } else {
  1341.         set flag FILE
  1342.         }
  1343.         cd $appPWD
  1344.     } else {
  1345.         set directory $dirname
  1346.         set file [file tail $path]
  1347.         set flag PATH
  1348.     }
  1349.     }
  1350.  
  1351.     return [list $flag $directory $file]
  1352. }
  1353.  
  1354.  
  1355. # Gets called when the entry box gets keyboard focus. We clear the selection
  1356. # from the icon list . This way the user can be certain that the input in the 
  1357. # entry box is the selection.
  1358. #
  1359. proc tkFDialog_EntFocusIn {w} {
  1360.     upvar #0 [winfo name $w] data
  1361.  
  1362.     if [string compare [$data(ent) get] ""] {
  1363.     $data(ent) selection from 0
  1364.     $data(ent) selection to   end
  1365.     $data(ent) icursor end
  1366.     } else {
  1367.     $data(ent) selection clear
  1368.     }
  1369.  
  1370.     tkIconList_Unselect $data(icons)
  1371.  
  1372.     if ![string compare $data(type) open] {
  1373.     $data(okBtn) config -text "Open"
  1374.     } else {
  1375.     $data(okBtn) config -text "Save"
  1376.     }
  1377. }
  1378.  
  1379. proc tkFDialog_EntFocusOut {w} {
  1380.     upvar #0 [winfo name $w] data
  1381.  
  1382.     $data(ent) selection clear
  1383. }
  1384.  
  1385.  
  1386. # Verification procedure
  1387. proc tkFDialog_VerifyFileName { w fname } {
  1388.     upvar #0 [winfo name $w] data
  1389.  
  1390.     set list [tkFDialogResolveFile $data(selectPath) $fname \
  1391.           $data(-defaultextension)]
  1392.     set flag [lindex $list 0]
  1393.     set path [lindex $list 1]
  1394.     set file [lindex $list 2]
  1395.  
  1396.     case $flag {
  1397.     OK {
  1398.         if ![string compare $file ""] {
  1399.             tkFDialog_SetPathSilently $w [file dirname $path]
  1400.             # CYGNUS LOCAL: handle choosedir
  1401.             if {$data(-choosedir)} {
  1402.             if {$data(-multiple)} {
  1403.                 lappend data(selectFile) [file tail $path]
  1404.             } else {
  1405.                 set data(selectFile) [file tail $path]
  1406.             }
  1407.           tkFDialog_Done $w
  1408.         } else {
  1409.             # user has entered an existing (sub)directory
  1410.             set data(selectPath) $path
  1411.             $data(ent) delete 0 end
  1412.         }
  1413.         } else {
  1414.         tkFDialog_SetPathSilently $w $path
  1415.         if {$data(-multiple)} {
  1416.             lappend data(selectFile) $file
  1417.         } else {
  1418.             set data(selectFile) $file
  1419.         }
  1420.         tkFDialog_Done $w
  1421.         }
  1422.     }
  1423.     PATTERN {
  1424.         set data(selectPath) $path
  1425.         set data(filter) $file
  1426.     }
  1427.     FILE {
  1428.         if ![string compare $data(type) open] {
  1429.         tk_messageBox -icon warning -type ok -parent $data(-parent) \
  1430.             -message "File \"[file join $path $file]\" does not exist."
  1431.         $data(ent) select from 0
  1432.         $data(ent) select to   end
  1433.         $data(ent) icursor end
  1434.         } else {
  1435.         tkFDialog_SetPathSilently $w $path
  1436.         if {$data(-multiple)} {
  1437.             lappend data(selectFile) $file
  1438.         } else {
  1439.             set data(selectFile) $file
  1440.         }
  1441.         tkFDialog_Done $w
  1442.         }
  1443.     }
  1444.     PATH {
  1445.         tk_messageBox -icon warning -type ok -parent $data(-parent) \
  1446.         -message "Directory \"$path\" does not exist."
  1447.         $data(ent) select from 0
  1448.         $data(ent) select to   end
  1449.         $data(ent) icursor end
  1450.     }
  1451.     CHDIR {
  1452.         tk_messageBox -type ok -parent $data(-parent) -message \
  1453.            "Cannot change to the directory \"$path\".\nPermission denied."\
  1454.         -icon warning
  1455.         $data(ent) select from 0
  1456.         $data(ent) select to   end
  1457.         $data(ent) icursor end
  1458.     }
  1459.     ERROR {
  1460.         tk_messageBox -type ok -parent $data(-parent) -message \
  1461.            "Invalid file name \"$path\"."\
  1462.         -icon warning
  1463.         $data(ent) select from 0
  1464.         $data(ent) select to   end
  1465.         $data(ent) icursor end
  1466.     }
  1467.     }
  1468. }
  1469.  
  1470. # Gets called when user presses Return in the "File name" entry.
  1471. #
  1472. proc tkFDialog_ActivateEnt {w} {
  1473.     upvar #0 [winfo name $w] data
  1474.  
  1475.     #set text [string trim [$data(ent) get]]
  1476.     set text [$data(ent) get]
  1477.     if {$data(-multiple)} {
  1478.     set data(selectFile) ""
  1479.     foreach fname $text {
  1480.         tkFDialog_VerifyFileName $w $fname
  1481.     }
  1482.     } else {
  1483.     tkFDialog_VerifyFileName $w $text
  1484.     }
  1485. }
  1486.  
  1487. # Gets called when user presses the Alt-s or Alt-o keys.
  1488. #
  1489. proc tkFDialog_InvokeBtn {w key} {
  1490.     upvar #0 [winfo name $w] data
  1491.  
  1492.     if ![string compare [$data(okBtn) cget -text] $key] {
  1493.     tkButtonInvoke $data(okBtn)
  1494.     }
  1495. }
  1496.  
  1497. # Gets called when user presses the "parent directory" button
  1498. #
  1499. proc tkFDialog_UpDirCmd {w} {
  1500.     upvar #0 [winfo name $w] data
  1501.  
  1502.     if [string compare $data(selectPath) "/"] {
  1503.     set data(selectPath) [file dirname $data(selectPath)]
  1504.     }
  1505. }
  1506.  
  1507. # Join a file name to a path name. The "file join" command will break
  1508. # if the filename begins with ~
  1509. #
  1510. proc tkFDialog_JoinFile {path file} {
  1511.     if {[string match {~*} $file] && [file exists $path/$file]} {
  1512.     return [file join $path ./$file]
  1513.     } else {
  1514.     return [file join $path $file]
  1515.     }
  1516. }
  1517.  
  1518.  
  1519.  
  1520. # Gets called when user presses the "OK" button
  1521. #
  1522. proc tkFDialog_OkCmd {w} {
  1523.     upvar #0 [winfo name $w] data
  1524.  
  1525.     set text [tkIconList_Get $data(icons)]
  1526.     if [string compare $text ""] {
  1527.     if {!$data(-multiple)} {
  1528.         set file [tkFDialog_JoinFile $data(selectPath) $text]
  1529.         # CYGNUS LOCAL: handle choosedir
  1530.         if {!$data(-choosedir) && [file isdirectory $file]} {
  1531.         tkFDialog_ListInvoke $w $text
  1532.         return
  1533.         }
  1534.     }
  1535.     }
  1536.  
  1537.     tkFDialog_ActivateEnt $w
  1538. }
  1539.  
  1540. # Gets called when user presses the "Cancel" button
  1541. #
  1542. proc tkFDialog_CancelCmd {w} {
  1543.     upvar #0 [winfo name $w] data
  1544.     global tkPriv
  1545.  
  1546.     set tkPriv(selectFilePath) ""
  1547. }
  1548.  
  1549. # Gets called when user browses the IconList widget (dragging mouse, arrow
  1550. # keys, etc)
  1551. #
  1552. proc tkFDialog_ListBrowse {w text} {
  1553.     upvar #0 [winfo name $w] data
  1554.  
  1555.     if {$text == ""} {
  1556.     return
  1557.     }
  1558.  
  1559.     set file [tkFDialog_JoinFile $data(selectPath) $text]
  1560.     # CYGNUS LOCAL: handle choosedir
  1561.     if {$data(-choosedir) || ![file isdirectory $file]} {
  1562.     $data(ent) delete 0 end
  1563.     $data(ent) insert 0 $text
  1564.  
  1565.     if ![string compare $data(type) open] {
  1566.         $data(okBtn) config -text "Open"
  1567.     } else {
  1568.         $data(okBtn) config -text "Save"
  1569.     }
  1570.     } else {
  1571.     $data(okBtn) config -text "Open"
  1572.     }
  1573. }
  1574.  
  1575. # Gets called when user invokes the IconList widget (double-click, 
  1576. # Return key, etc)
  1577. #
  1578. proc tkFDialog_ListInvoke {w text} {
  1579.     upvar #0 [winfo name $w] data
  1580.  
  1581.     if {$text == ""} {
  1582.     return
  1583.     }
  1584.  
  1585.     if {$data(-multiple)} {
  1586.     set file [tkFDialog_JoinFile $data(selectPath) [lindex $text 0]]
  1587.     } else {
  1588.     set file [tkFDialog_JoinFile $data(selectPath) $text]
  1589.     }
  1590.  
  1591.     if [file isdirectory $file] {
  1592.     set appPWD [pwd]
  1593.     if [catch {cd $file}] {
  1594.         tk_messageBox -type ok -parent $data(-parent) -message \
  1595.            "Cannot change to the directory \"$file\".\nPermission denied."\
  1596.         -icon warning
  1597.     } else {
  1598.         cd $appPWD
  1599.         set data(selectPath) $file
  1600.     }
  1601.     } else {
  1602.         if {$data(-multiple)} {
  1603.         set data(selectFile) [list $file]
  1604.     } else {
  1605.         set data(selectFile) $file
  1606.     }
  1607.     tkFDialog_Done $w
  1608.     }
  1609. }
  1610.  
  1611. # tkFDialog_Done --
  1612. #
  1613. #    Gets called when user has input a valid filename.  Pops up a
  1614. #    dialog box to confirm selection when necessary. Sets the
  1615. #    tkPriv(selectFilePath) variable, which will break the "tkwait"
  1616. #    loop in tkFDialog and return the selected filename to the
  1617. #    script that calls tk_getOpenFile or tk_getSaveFile
  1618. #
  1619. proc tkFDialog_Done {w {selectFilePath ""}} {
  1620.     upvar #0 [winfo name $w] data
  1621.     global tkPriv
  1622.  
  1623.     if ![string compare $selectFilePath ""] {
  1624.     if {$data(-multiple)} {
  1625.         set selectFilePath {}
  1626.         foreach f $data(selectFile) {
  1627.         lappend selectFilePath [file join $data(selectPath) $f]
  1628.         }
  1629.     } else {
  1630.         set selectFilePath [tkFDialog_JoinFile $data(selectPath) \
  1631.             $data(selectFile)]
  1632.     }
  1633.     set tkPriv(selectFile)     $data(selectFile)
  1634.     set tkPriv(selectPath)     $data(selectPath)
  1635.  
  1636.     if {[file exists $selectFilePath] && 
  1637.         ![string compare $data(type) save]} {
  1638.  
  1639.         set reply [tk_messageBox -icon warning -type yesno -parent $data(-parent) \
  1640.             -message "File \"$selectFilePath\" already exists.\nDo you want to overwrite it?"]
  1641.         if ![string compare $reply "no"] {
  1642.         return
  1643.         }
  1644.     }
  1645.     }
  1646.     set tkPriv(selectFilePath) $selectFilePath
  1647. }
  1648.  
  1649.